mod test;

use std::f32::consts::PI;
use std::fs::File;
use std::io;
use std::io::{stdout, Read};
use std::ptr::null;

fn main() {
    println!("Hello, world!");
    println!("{}", "Hello, world!");
    println!("a is {}, a also is {}", 10, 10);
    println!("{0}, {0}", "a");
    println!("{{}}");
    let b = 10;
    println!("{0}, {0}", b);
    let mut b1 = 20;
    println!("{0}", b1);
    b1 = 30;
    println!("{0}", b1);
    let x = 10;
    let x = x + 1;
    let x = x * 2;
    println!("{0}", x);
    let c1: u8 = 123;
    let c2: u16 = 12_000;
    let c3: u32 = 1_000_000;
    let c4: u64 = 1_000_000_000;
    let d1 = 0x123;
    let d2 = 0o123;
    println!("{0}, {1}, {2}, {3}, {4}, {5}", c1, c2, c3, c4, d1, d2);
    let mut sum = 5 + 10;
    sum += 5;
    println!("{0}", sum);
    let difference = 95.5 - 4.3;
    println!("{0}", difference);
    let product = 4 * 30;
    println!("{0}", product);
    let quotient = 56.7 / 32.2;
    println!("{0}", quotient);
    let remainder = 43 % 5;
    println!("{0}", remainder);
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let (x, y, z) = tup;
    println!("{0}, {1}, {2}", x, y, z);
    let five_hundred = tup.0;
    let six_point_four = tup.1;
    let one = tup.2;
    println!("{0}, {1}, {2}", five_hundred, six_point_four, one);
    let a = [1, 2, 3, 4, 5];
    let b = ["one","two"];
    let c:[i32;5] = [1,2,3,4,5];
    let d = [3;5];
    println!("{0}, {1}, {2}, {3}, {4}", a[0], a[1], a[2], a[3], a[4]);
    for i in a.iter() {
        println!("{0}", i);
    }
    for i in 0..5 {
        println!("{0}", i);
    }
    for i in (0..5).rev() {
        println!("{0}", i);
    }
    for i in b.iter() {
        println!("{0}", i);
    }
    for i in c.iter() {
        println!("{0}", i);
    }
    for i in d.iter() {
        println!("{0}", i);
    }
    for i in 0..5 {
        println!("{0}", i);
    }
    for i in 0..=5 {
        println!("{0}", i);
    }

    let first = a[0];
    let second = a[1];
    println!("{0}, {1}", first, second);

    let mut a = [1,2,3,4,5];
    a[0] = 11;

    println!("1+2={0}", add(1,2));
    println!("Rust 中可以在一个用 {{}} 包括的块里编写一个较为复杂的表达式");
    let y = {
        let x = 3;
        x + 1
    };
    println!("{0}", y);
    println!("在 Rust 中，函数定义可以嵌套");
    fn five()->i32 {
        5
    }
    println!("{0}", five());
    //Rust 不支持自动返回值类型判断！如果没有明确声明函数返回值的类型，函数将被认为是"纯过程"，
    // 不允许产生返回值，return 后面不能有返回值表达式。这样做的目的是为了让公开的函数能够形成
    // 可见的公报
    fn plus_one(x: i32) -> i32 {
        x + 1
    }
    println!("{0}", plus_one(5));
    fn print_something() {
        println!("Something");
    }
    print_something();
    fn print_something_else() -> () {
        println!("Something else");
    }
    print_something_else();
    //在Rust语言中的条件语句
    let condition = true;
    let number = if condition {
        5
    }
    else {
        6
    };
    println!("{0}", number);

    let mut number = 3;
    while number != 0 {
        println!("{0}", number);
        number -= 1;
    }
    loop {
        println!("{0}", number);
        number -= 1;
        if number == -3 {
            break;
        }
    }
    //循环
    //rev() 倒序迭代的循环结构
    //(1..4) Rust 标准库提供的 Range 功能
    //创建一个从 1 开始到 4 结束的区间（不包含 4）
    for number in (1..4).rev() {
        println!("{0}", number);
    }
    // if else
    if number > 5 {
        println!("{0} > 5", number);
    } else {
        println!("{0} <= 5", number);
    }
    if number % 4 == 0 {
        println!("{0} % 4 == 0", number);
    } else if number % 3 == 0 {
        println!("{0} % 3 == 0", number);
    } else if number % 2 == 0 {
        println!("{0} % 2 == 0", number);
    } else {
        println!("{0} % 2 != 0", number);
    }
    //三目运算符
    //if <condition> { block 1 } else { block 2 }
    let number = if number > 5 {1}else { 0 };
    println!("{0}", number);
    //for循环
    for number in 0..5 {
        println!("{0}", number);
    }
    //遍历一个线性数据据结构（比如数组）
    for number in [10,20,30,40,50].iter() {
        println!("{0}", number);
    }

    println!("{0}", find_char(['a','b','c','d','e'], 'f'));

    let str = "hello world";
    println!("{0}中出现的次数={1}",str, count_char_num(str));
    // 所有权有以下三条规则：
    // Rust 中的每个值都有一个变量，称为其所有者。
    // 一次只能有一个所有者。
    // 当所有者不在程序运行范围时，该值将被删除。
    // 这三条规则是所有权概念的基础。
    //变量与数据交互方式主要有移动(Move)和克隆(Clone)两种
    let s1 = String::from("hello");
    let s2 = s1;
    println!("{0}", s2);
    // println!("{0}", s1); 移动后使用的值 [E0382]
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("{0}", s1);
    println!("{0}", s2);

    //函数参数和返回值
    let s1 = gives_ownership();

    let s2 = takes_and_gives_back(s1);
    println!("{0}", s2);
    // 引用与租借
    // 引用(Reference)是 C++ 开发者较为熟悉的概念。
    // 如果你熟悉指针的概念，你可以把它看作一种指针。
    // 实质上"引用"是变量的间接访问方式
    let s1 = String::from("hello");
    let s2 = &s1;
    println!("s1 is {}, s2 is {}", s1, s2);

    println!("the length of {} is {}",s1, calculate_length(s2));
    //引用可变的租借方式，就像你租一个房子，如果物业指定房主可以修改房子结构，房主在租借时也
    // 在合同中声明赋予你这种权利，你是可以重新装修房子的
    let mut s = String::from("hello");
    let s2 = &mut s;
    s2.push_str(" world");
    println!("{}", s2);
    //Rust Slice（切片）类型
    let s = String::from("hello world");
    //  ptr 0 length 5-0
    let part1 = &s[0..5];
    //  ptr 6 length 11-6
    let part2 = &s[6..11];
    println!("{0}, {1}", part1, part2);
    // 使用 .. 表示范围的语法在循环章节中出现过。x..y 表示 [x, y) 的数学含义。.. 两边可以
    // 没有运算数：
    // ..y 等价于 0..y
    // x.. 等价于位置 x 到数据结束
    //     .. 等价于位置 0 到结束
    for i in 0..5 {
        println!("{0}", i);
    }
    let rect = Rectangle { width: 30, height: 50 };
    println!("rect1 is {:?}", rect);
    println!("rect1 is {:#?}", rect);
    // 结构体方法
    // 方法(Method)和函数(Function)类似，只不过它是用来操作结构体示例的。
    // 如果你学习过一些面向对象的语言，那你一定很清楚函数一般放在类定义里并在函数中用 this 表示所操作的示例。
    // Rust 语言不是面向对象的，从它所有权机制的创新可以看出这一点。但是面向对象的珍贵思想可以在 Rust 实现。
    // 结构体方法的第一个参数必须是 &self，不需声明类型，因为 self 不是一种风格而是关键字。

    let rect1 = Rectangle { width: 40, height: 50 };
    //请注意，在调用结构体方法的时候不需要填写 self ，这是出于对使用方便性的考虑。
    println!("rect1 area is {}", rect1.area());
    println!("rect is wider than rect1 : {}", rect.wider(&rect1));
    // 创建结构体实例
    let rect2 = Rectangle::create(30, 50);
    // 贴士：结构体 impl 块可以写几次，效果相当于它们内容的拼接！
    // 单元结构体
    // 结构体可以只作为一种象征而无需任何成员：
    struct UnitStruct;
    // 我们称这种没有身体的结构体为单元结构体（Unit Struct）。
    let book = Book::Papery{index:1001};
    match book{
        Book::Papery{index} => {println!("index is {}", index);},
        Book::Electronic{url} => {println!("url is {}", url);}
    }
    let ebook = Book::Electronic{url:String::from("url://...")};
    match ebook{
        Book::Papery{index} => {println!("index is {}", index);},
        Book::Electronic{url} => {println!("url is {}", url);}
    }
    let t = "123";
    match t{
        "123"=>println!("Yes"),
        _=> {}
    }
    // Option
    // Option 是 Rust 标准库中的枚举类，这个类用于填补 Rust 不支持 null 引用的空白。
    // 许多语言支持 null 的存在（C/C++、Java），这样很方便，但也制造了极大的问题，null 的发明者也承认这一点，"一个方便的想法造成累计 10 亿美元的损失"。
    // null 经常在开发者把一切都当作不是 null 的时候给予程序致命一击：毕竟只要出现一个这样的错误，程序的运行就要彻底终止。
    // 为了解决这个问题，很多语言默认不允许 null，但在语言层面支持 null 的出现（常在类型前面用 ? 符号修饰）。
    // Java 默认支持 null，但可以通过 @NotNull 注解限制出现 null，这是一种应付的办法。
    // Rust 在语言层面彻底不允许空值 null 的存在，但无奈null 可以高效地解决少量的问题，所以 Rust 引入了 Option 枚举类：
    let opt: Option<&str> = Option::None;

    match opt {

        Option::Some(something) => {

            println!("{}", something);

        },

        Option::None => {

            println!("opt is nothing");

        }
    }
    let t = Option::Some(64);
    match t {
        Option::Some(64) => println!("Yes"),
        _=> println!("No")
    }

    //判断 i 是否是数字 0，如果是就打印 zero。
    let i = 0;

    match i {

        0 => println!("zero"),

        _ => {},

    }
    // if let 匹配值 = 源变量 {
    //     语句块
    // }
    let i = 0;
    if let 0=i{
        println!("zero")
    }
    if let Option::Some(64)=t{
        println!("Yes")
    } else {
        println!("No")
    }
    //所有的系统库模块都是被默认导入的，
    // 所以在使用的时候只需要使用 use 关键字简化路径就可以方便的使用了。
    println!("{}",(PI/2.0).sin());
    // Rust 有一套独特的处理异常情况的机制，它并不像其它语言中的 try 机制那样简单。
    // 首先，程序中一般会出现两种错误：可恢复错误和不可恢复错误。
    // 可恢复错误的典型案例是文件访问错误，如果访问一个文件失败，有可能是因为它正在被占用，
    // 是正常的，我们可以通过等待来解决。
    // 但还有一种错误是由编程中无法解决的逻辑错误导致的，例如访问数组末尾以外的位置。
    // 大多数编程语言不区分这两种错误，并用 Exception （异常）类来表示错误。在 Rust 中没有 Exception。
    // 对于可恢复错误用 Result<T, E> 类来处理，对于不可恢复错误使用 panic! 宏来处理。
    //panic!("error occured");
    println!("Hello, rust!");
    // 此概念十分类似于 Java 编程语言中的异常。实际上在 C 语言中我们就常常将函数返回值设置
    // 成整数来表达函数遇到的错误，在 Rust 中通过 Result<T, E> 枚举类作返回值来进行异常表达：
    // enum Result<T, E> {
    //     Ok(T),
    //     Err(E),
    // }
    let f = File::open("file.txt");
    match f {
        Ok(file) => {
            println!("File opened success!");
        },
        Err(error) => {
            println!("Failed to open the file!");
        }
    }
    if let Ok(file) = File::open("file.txt") {
        println!("File opened success!");
    } else {
        println!("Failed to open the file!");
    }
    //let f1 = File::open("hello.txt").unwrap();
    //let f2 = File::open("hello.txt").expect("Failed to open.");
    // 这段程序相当于在 Result 为 Err 时调用 panic! 宏。两者的区别在于 expect 能够向
    // panic! 宏发送一段指定的错误信息。


    //? 符的实际作用是将 Result 类非异常的值直接取出，如果有异常就将异常 Result 返回出去。
    // 所以，? 符仅用于返回值类型为 Result<T, E> 的函数，其中 E 类型必须和 ? 所处理的
    // Result 的 E 类型一致。

    let r = g(10000);
    if let Ok(t) = r {
        println!("{}", t);
    } else {
        println!("Error!");
    }

    // 我们完全可以把 try 块在独立的函数中实现，将所有的异常都传递出去解决。实际上这才是一个
    // 分化良好的程序应当遵循的编程方法：应该注重独立功能的完整性。
    // 但是这样需要判断 Result 的 Err 类型，获取 Err 类型的函数是 kind()。
    let str_file = read_text_from_file("hello.txt");
    match str_file {
        Ok(s) => println!("{}", s),
        Err(e) => {
            match e.kind() {
                io::ErrorKind::NotFound => {
                    println!("No such file");
                },
                _ => {
                    println!("Cannot read the file");
                }
            }
        }
    }
    //泛型
    let p1 = Point { x: 1, y: 2 };
    let p2 = Point { x: 1.0, y: 2.0 };
    println!("{:?}", p1.x);
    println!("{:?}", p2.x);
    // 特性
    // 特性(trait)概念接近于 Java 中的接口(Interface)，但两者不完全相同。特性与接口相同的地方在于它们都是一种行为规范，可以用于标识哪些类有哪些方法。
    // 特性在 Rust 中用 trait 表示：

    // Descriptive 指定了实现者必须有是 describe(&self) -> String 方法。
    let carli = Person {
        name: String::from("Carli"),
        age: 18,
    };
    println!("{}", carli.describe());
    let s1 = "helloworld";
    let s2 = "rust";
    println!("{} is longer", longer(s1, s2));
}
fn longer<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.len() > b.len() {
        a
    } else {
        b
    }
}
trait Descriptive {
    fn describe(&self) -> String{
        String::from("[object]")
    }
}
// impl <特性名> for <所实现的类型名>
// Rust 同一个类可以实现多个特性，每个 impl 块只能实现一个。
struct Person{
    name: String,
    age: i32,
}
impl Descriptive for Person{
    fn describe(&self) -> String {
        format!("{} is {} years old", self.name, self.age)
    }
}
struct Point<T> {
    x: T,
    y: T,
}
impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
    fn y(&self) -> &T {
        &self.y
    }
}
fn read_text_from_file(path: &str) -> Result<String, io::Error> {
    let mut f = File::open(path)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

fn f(i: i32) -> Result<i32, bool> {
    if i >= 0 { Ok(i) }
    else { Err(false) }
}

fn g(i: i32) -> Result<i32, bool> {
    let t = f(i)?;
    Ok(t) // 因为确定 t 不是 Err, t 在这里已经是 i32 类型
}

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

#[derive(Debug)]
enum Book{
    Papery{index:i32},Electronic {url: String}
}
// match 语法
// 枚举的目的是对某一类事物的分类，分类的目的是为了对不同的情况进行描述。基于这个原理，
// 往往枚举类最终都会被分支结构处理（许多语言中的 switch ）。 switch 语法很经典，
// 但在 Rust 中并不支持，很多语言摒弃 switch 的原因都是因为 switch 容易存在因忘记
// 添加 break 而产生的串接运行问题，Java 和 C# 这类语言通过安全检查杜绝这种情况出现。
// Rust 通过 match 语句来实现分支结构。先认识一下如何用 match 处理枚举类：


impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
    fn wider(&self, rect: &Rectangle)-> bool{
        if self.width > rect.width {
            return true;
        } else {
           return false;
        }
    }
    // 结构体关联函数
    // 之所以"结构体方法"不叫"结构体函数"是因为"函数"这个名字留给了这种函数：它在 impl 块中
    // 却没有 &self 参数。
    // 这种函数不依赖示例，但是使用它需要声明是在哪个 impl 块中的。
    // 一直使用的 String::from 函数就是一个"关联函数"。
    fn create(width: u32, height: u32)-> Rectangle{
        Rectangle{width,height}
    }

}
fn calculate_length(s: &String) -> usize{
    s.len()
}

fn count_char_num(str: &str) -> i32{
    //查找字符串中o出现的次数
    let mut count = 0;
    for c in str.chars() {
        if c == 'o' {
            count += 1;
        }
    }
    return count;
}

//实现一个函数查找一个字符数组中的某个字符
fn find_char(array: [char;5], c: char) -> usize {
    for (index, value) in array.iter().enumerate() {
        if *value == c {
            return index;
        }
    }
    return usize::MAX;
}
fn add(a: i32, b: i32) -> i32 {
    return a + b;
}

fn gives_ownership() -> String {
    let some_string = String::from("hello");
    // some_string 被声明有效

    return some_string;
    // some_string 被当作返回值移动出函数
}

fn takes_and_gives_back(a_string: String) -> String {
    // a_string 被声明有效

    a_string  // a_string 被当作返回值移出函数
}
